package src;

import java.awt.Color;
import java.awt.List;
import java.util.Arrays;
import java.util.Scanner;

import javax.swing.DefaultListModel;
import javax.swing.JList;


/**
 * All the logic of SuperTicTacToe (rules, players, turns) is all housed in
 * Game. Game communicates with the GUI via the Controller and interprets all
 * moves made by Players (including if they are valid). Delegates tasks to
 * Board, such as keeping track of which spots are occupied on the grid, or
 * whether or not the grid has been won, etc.
 * 
 * @author Akshat, Anurag
 * 
 */
public class NetworkedSuperTicTacToeGame extends SuperTicTacToeGame
                implements
                NetworkClient
{
    /**
     * Contains all the players.
     */
    private NetworkPlayer[] players;

    /**
     * The default port.
     */
    public static final int DEFAULT_PORT = 1337;

    /**
     * Holds the port that we're connecting to.
     */
    private int port = DEFAULT_PORT;

    /**
     * The object that handles networking.
     */
    protected NetworkManager network;

    /** Data model for connections list */
    protected DefaultListModel connModel;

    /** List of active connections */
    protected JList connections;

    /**
     * Contains the location of the other player.
     */
    private GridLocation networkLocation = null;

    /**
     * The ip that we're connecting to.
     */
    private String ip;


    /**
     * Constructor that constructs the game, plus all associated networking
     * items.
     * 
     * @param c
     */
    public NetworkedSuperTicTacToeGame( SuperTicTacToeController c )
    {

        super( c );

        board = new Board();
        currSubGrid = board.get( 1, 1 ); // start game in center of board.

        Scanner scan = new Scanner( System.in );

        connModel = new DefaultListModel();

        connections = new JList( connModel );

        System.out.print( "Enter IP: " );
        ip = scan.nextLine();

        if ( ip.length() == 0 )
        {
            ip = "127.0.0.1";

        }

        System.out.print( "Enter receive port: " );
        port = scan.nextInt();

        // create a networking object to peform I/O
        network = new NetworkManager( this, port );

        System.out.print( "Enter send port: " );
        port = scan.nextInt();
        connect();

        players = new NetworkPlayer[2];

        if ( network.master )// first.indexOf( "Y" ) >= 0 || first.indexOf( "y"
                             // ) >= 0)
        {
            players[0] = new NetworkPlayer( "Local",
                new Color( 19, 223, 213 ),
                this,
                true );
            players[1] = new NetworkPlayer( "Remote",
                new Color( 19, 100, 213 ),
                this,
                false );
        }
        else
        {

            players[1] = new NetworkPlayer( "Local",
                new Color( 19, 100, 213 ),
                this,
                true );
            players[0] = new NetworkPlayer( "Remote",
                new Color( 19, 223, 213 ),
                this,
                false );
        }

        currPlayerIndex = 0;
    }


    /**
     * Some of the main logic of the game. Checks if move is valid, specifically
     * if it is in the correct subGrid. If all tests are passed, the current
     * player's location is set, allowing player.getPlay() to run, which allows
     * player.play() to run which allows the game to progress.
     * 
     * @param loc
     *            Location on board which was clicked.
     */
    public void moveMade( GridLocation loc )
    {

        if ( currSubGrid != null && !currSubGrid.canPlay() )
        {
            currSubGrid = null;
        }

        if ( players[currPlayerIndex] instanceof NetworkPlayer )
        {
            if ( currSubGrid == null
                || ( loc.getMainRow() == currSubGrid.getMainRow() && loc.getMainCol() == currSubGrid.getMainCol() ) )
            {
                if ( currSubGrid != null
                    && currSubGrid.getOpenLocations().contains( loc ) )
                {
                    players[currPlayerIndex].setPlay( loc );
                    currSubGrid = board.get( loc.getSubRow(), loc.getSubCol() );

                    if ( players[currPlayerIndex].getLocal() )
                    {
                        System.out.println( "SENDING: " + loc.networkToString() );
                        network.send( loc.networkToString() );
                    }
                }
                else if ( currSubGrid == null
                    && board.get( loc.getMainRow(), loc.getMainCol() )
                        .getOpenLocations()
                        .contains( loc ) )
                {
                    players[currPlayerIndex].setPlay( loc );
                    currSubGrid = board.get( loc.getSubRow(), loc.getSubCol() );

                    if ( players[currPlayerIndex].getLocal() )
                    {
                        System.out.println( "SENDING: " + loc.networkToString() );
                        network.send( loc.networkToString() );
                    }

                }
                else
                {
                    System.out.println( "Invalid spot!" );
                    return;
                }
            }
            else
            {
                System.out.println( "Incorrect Grid!" );
                return;
            }
        }
        else
        {
            currSubGrid = board.get( loc.getSubRow(), loc.getSubCol() );
        }

        if ( currSubGrid != null && !currSubGrid.canPlay() )
        {
            currSubGrid = null;
        }
    }


    /**
     * (non-Javadoc)
     * 
     * @see src.NetworkClient#remoteMove(src.SocketName, java.lang.String)
     *      Override of the method from network client. Gets the incoming
     *      message, parses it into a grid location, and passes it to the
     *      player.
     * @param name
     *            of the socket
     * @param message
     *            that is incoming
     */
    @Override
    public void remoteMove( SocketName name, String message )
    {
        System.out.println( "Socket: " + name + "Message: " + message );

        System.out.println( "Receiving: " + message );

        java.util.List<String> items = Arrays.asList( message.split( "\\s*,\\s*" ) );

        networkLocation = new GridLocation( Integer.parseInt( items.get( 0 ) ),
            Integer.parseInt( items.get( 1 ) ),
            Integer.parseInt( items.get( 2 ) ),
            Integer.parseInt( items.get( 3 ) ) );

        System.out.println( networkLocation.toString() );

        moveMade( networkLocation );

    }


    @Override
    public void createSocket( SocketName name )
    {
        connModel.addElement( name );

    }


    @Override
    public void destroySocket( SocketName name )
    {
        if ( connModel.contains( name ) )
        {
            connModel.removeElement( name );
        }

    }


    /**
     * Helper method to read inputs from GUI components and create a new socket
     * connection.
     */
    protected void connect()
    {
        try
        {
            SocketName sock = new SocketName( ip, port + "", "port_" + port );

            if ( connModel.contains( sock ) )
            {
                System.out.println( "Cannot connect to " + sock
                    + ": already connected" );
            }
            else
            {
                network.connect( sock );
                System.out.println( "Connected to " + sock );
            }
        }
        catch ( IllegalArgumentException iae )
        {
            System.out.println( "Cannot connect: " + iae.getMessage() );
        }

    }


    /**
     * Disconnects the game from the network.
     */
    protected void disconnect()
    {
        int index = connections.getSelectedIndex();
        if ( index > -1 )
        {
            SocketName dead = (SocketName)( connModel.elementAt( index ) );

            network.disconnect( dead );
        }
    }


    /**
     * (non-Javadoc)
     * 
     * @see src.SuperTicTacToeGame#getCurrentPlayer() Returns the current
     *      player.
     */
    public Player getCurrentPlayer()
    {
        return players[currPlayerIndex];
    }


    /**
     * @return reference to game board
     */
    public Board getBoard()
    {
        return board;
    }

}
